use std::io::{Result};
use std::fmt;

use super::cpblock::CPBlock;
use super::head::Headed;
use super::addons::{Addons,Addon};
use super::umutex::{Umutex,ScopedLock};
use super::head::AHead;
use super::mbox::MBox;
use super::ubox::UBox;
use super::layout;
use super::bmem::{Bar,BarInc};
use super::bfifo::{self,BufSized,FifoPoint,FifoPointer,UsizeArray,UAIndex};

impl BufSized for layout::C {
    const APP_LEN:usize = 16;
    const HEAD_LEN:usize = std::mem::size_of::<FifoShare<layout::C>>();
    const BUF_LEN:usize = (layout::ADDONEND-layout::ADDONPOST-Self::HEAD_LEN)/std::mem::size_of::<usize>()-Self::APP_LEN; 
    const HEAD_START:usize = layout::ADDONPOST;
    type BufArray = UsizeArray<{Self::BUF_LEN}>;
    type AppArray = UsizeArray<{Self::APP_LEN}>;
}
impl BufSized for layout::R {
    const APP_LEN:usize = 16;
    const HEAD_LEN:usize = std::mem::size_of::<FifoShare<layout::R>>();
    const BUF_LEN:usize = (layout::ADDONEND-layout::ADDONPOST-Self::HEAD_LEN)/std::mem::size_of::<usize>()-Self::APP_LEN; 
    const HEAD_START:usize = layout::ADDONPOST;
    type BufArray = UsizeArray<{Self::BUF_LEN}>;
    type AppArray = UsizeArray<{Self::APP_LEN}>;
}

#[repr(C)]
#[derive(Debug)]
pub struct FifoShare<L:BufSized> {
    pub bar:Bar,
    point:FifoPoint<L>,
    _marker : std::marker::PhantomData<L>,
}

impl<L:BufSized> fmt::Display for FifoShare<L> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"bar:{} sp:{} rp:{}",self.bar,self.point.sp,self.point.rp)
    }
}

#[repr(C)]
#[derive(Debug)]
pub struct FifoData<L:BufSized> {
    pub meta:FifoShare<L>,
    pub buf:L::BufArray,
    pub appdata:L::AppArray,
}

pub struct AddonFifo<L:BufSized> {
    pub share:MBox<FifoData<L>>,
    local_rp : usize,
    _marker : std::marker::PhantomData<L>,
}

impl<L:BufSized> AddonFifo<L> {
    fn inc_local_rp(&mut self) {bfifo::inc::<L>(&mut self.local_rp);}
}
impl<L:BufSized> fmt::Display for FifoData<L> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.meta)
    }
}

impl<L:BufSized> BarInc for AddonFifo<L> {
    fn inc(&mut self,s :usize) -> usize {self.share.meta.bar.inc(s)}
    fn pos(&self) -> usize {self.share.meta.bar.pos}
}

impl<L:BufSized> Headed for AddonFifo<L> {
    type ehead = FifoData<L>;
}

impl Addons for AddonFifo<layout::R> {
    fn init<T:Headed>(cpb: &CPBlock<T>) -> Result<AddonFifo<layout::R>> {
        let share = AHead::<T>::addon::<FifoData<layout::R>>(&cpb.head)?; 
        Ok(AddonFifo::<layout::R>{
            share:share,
            local_rp:0,
            _marker : std::marker::PhantomData::<layout::R>::default(),
        }) 
    }
}

impl Addons for AddonFifo<layout::C> {
    fn init<T:Headed>(cpb: &CPBlock<T>) -> Result<AddonFifo<layout::C>> {
        let share = cpb.head.as_subtype::<FifoData<layout::C>>(layout::C::HEAD_START)?; 
        Ok(AddonFifo::<layout::C>{
            share:share,
            local_rp:0,
            _marker : std::marker::PhantomData::<layout::C>::default(),
        }) 
    }
}

impl<L:BufSized> FifoPointer for AddonFifo<L> {
	fn local_reset(&mut self) { self.local_rp = self.share.meta.point.sp;}
    fn sync_globe_rp(&mut self) {
        self.share.meta.point.set_rp(self.local_rp);
    }
	fn local_len(&self) -> usize { (L::BUF_LEN+self.share.meta.point.sp-self.local_rp)%L::BUF_LEN}
    fn globe_len(&self) -> usize { (L::BUF_LEN+self.share.meta.point.sp%L::BUF_LEN-self.share.meta.point.rp%L::BUF_LEN)%L::BUF_LEN}
    fn push(&mut self,off : usize) {
		if self.globe_len()==L::BUF_LEN-1 {
            self.share.meta.point.atomic_inc_rp();
        }
        let sp = self.share.meta.point.atomic_inc_sp();
        *self.share.buf.uaindex_mut(sp) = off;
    }            
    fn get_globe_rp(&mut self) -> usize {
        let rp = self.share.meta.point.atomic_inc_rp();
		*self.share.buf.uaindex(rp)
    }
    fn get_local_rp(&mut self) -> usize {
        *self.share.buf.uaindex(bfifo::inc::<L>(&mut self.local_rp))
    } 
}

impl<T:FifoPointer+Addons+BarInc+Headed> Addon<T> {
    pub fn local_get<P>(&mut self) -> Result<Option<MBox<P>>>{
		if self.addon.local_len()==0usize {
            Ok(None)
        } else {
            let o = self.addon.get_local_rp();
            let raw = self.get_mem(o);
            Ok(Some(MBox::<P>::attach(&raw,0usize)?))
        }
	}
    pub fn get<P>(&mut self) -> Result<Option<MBox<P>>>{
		if self.addon.globe_len()==0usize {
            Ok(None)
        } else {
            let o = self.addon.get_globe_rp();
            let raw = self.get_mem(o);
            Ok(Some(MBox::<P>::attach(&raw,0usize)?))
        }
	}
    pub fn push<P>(&mut self, p :&UBox<P>) {
        self.addon.push(p.offset())
    }
    pub fn sync(&mut self) { self.addon.sync_globe_rp() } 
    pub fn local_reset(&mut self) { self.addon.local_reset() }
    pub fn local_len(&self) -> usize { self.addon.local_len() }
    pub fn globe_len(&self) -> usize { self.addon.globe_len() }
}

pub type Fifo = Addon<AddonFifo<layout::R>>;
pub type CFifo = Addon<AddonFifo<layout::C>>;
